using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace PS.WebParts.Rollup.Core
{
    static class AggregateParser
    {
        internal static SetOperation Parse(string xml)
        {
            StringReader sr = new StringReader("<union id=\"root\">"+xml+"</union>");
            XmlTextReader xtr = new XmlTextReader(sr);
            xtr.WhitespaceHandling = WhitespaceHandling.None; 

            //Union union = new Union();
            List<SetOperation> ops = new  List<SetOperation>(1);
            Walk(xtr, ops, null);

            return ops[0];
        }

        private static void Walk(XmlTextReader xtr, List<SetOperation> ops, SetOperation parent)
        {
            while(xtr.Read() && xtr.IsStartElement())
            {
                string nodeName = xtr.LocalName;

                SetOperation op = null;
                switch(nodeName)
                {
                    case "join":
                        Join j = ParseJoin(xtr);
                        bool parentIsJoin = parent is Join;
                        if (!string.IsNullOrEmpty(j.BaseId) && parent != null && parentIsJoin)
                        {
                            throw new FormatException(
                                "'Left' attribute is not allowed in a child join.\n\n\nA child must only define a 'Right' attribute as the left part of the join is assumed to be the product of the parent join.\n\n The error is in the following join:\n" + j+"\n");
                        }

                        if (parentIsJoin)
                        {
                            j.BaseId = parent.BaseId;
                        }

                        if (parent != null && !((parent is Join) || parent is Union))
                            throw new FormatException(
                                "Invalid parent record. This node can only be placed within a Join or a Union tag: " +
                                xtr.ReadOuterXml());
                        op = j;
                        break;
                        
                    case "union":
                        Union u = ParseUnion(xtr);
                        if (parent != null && !(parent is Union))
                            throw new FormatException(
                                "Invalid parent record. This node can only be placed within a Union tag: " +
                                xtr.ReadOuterXml());
                        op = u;
                        break;

                    case "datasource":
                        DataTableRef dt = ParseDataTableRef(xtr);
                        if (parent != null && !(parent is Union))
                            throw new FormatException(
                                "Invalid parent record. This node can only be placed within a Union tag: " +
                                xtr.ReadOuterXml());
                        op = dt;
                        break;

                    case "Query":
                        Join pJoin = parent as Join;
                        if (pJoin != null)
                        {
                            pJoin.CorrelatedQuery = xtr.ReadInnerXml();
                        }
                        op = null;
                        break;
                        

                    default:
                        throw new FormatException("Unrecognized node name: " + nodeName + ": " + xtr.ReadOuterXml());
                }
                
                if (op != null)
                    ops.Add(op);

                xtr.MoveToElement();
                if (!xtr.IsEmptyElement)
                {
                    xtr.MoveToContent();
                    if (op!=null)
                    Walk(xtr, op.Children, op);
                }
                
                
            }
        }

        private static DataTableRef ParseDataTableRef(XmlTextReader xtr)
        {
            DataTableRef dtr = new  DataTableRef();

            if (xtr.MoveToAttribute("id"))
            {
                dtr.BaseId = xtr.Value;
            }
            return dtr;
        }

        private static Union ParseUnion(XmlTextReader xtr)
        {
            Union u = new Union();

            if (xtr.MoveToAttribute("id"))
            {
                u.BaseId = xtr.Value;
            }
            
            return u;
        }

        private static Join ParseJoin(XmlTextReader xtr)
        {
            Join j = new Join(string.Empty);
            
            if (xtr.MoveToAttribute("left"))
            {
                j.BaseId = xtr.Value;
            }

            if (!xtr.MoveToAttribute("right"))
                throw new FormatException("Required attribute missing: right" + ": " + xtr.ReadOuterXml());
            j.JoineeId = xtr.Value;

            if (xtr.MoveToAttribute("type"))
                j.JoinType = (Join.Type)Enum.Parse(typeof(Join.Type), xtr.Value, true);
            else
                j.JoinType = Join.Type.Inner;

            if (xtr.MoveToAttribute("correlate"))
                j.Correlate = (Join.CorrelationType)Enum.Parse(typeof(Join.CorrelationType), xtr.Value, true);
            else
                j.Correlate = Join.CorrelationType.None;

            if (xtr.MoveToAttribute("exclude"))
                j.ExcludeFields = xtr.Value;

            if (xtr.MoveToAttribute("include"))
                j.IncludeFields = xtr.Value;

            xtr.MoveToAttribute("on");
            string on = xtr.Value;

            string[] arr = on.Split('=');
            if (arr.Length != 2)
                throw new FormatException("Uncrecoginzed join expression :" + on + ": " + xtr.ReadOuterXml());

            j.BaseColumn = arr[0];
            j.JoineeColumn = arr[1];

            ////Rework the 
            //xtr.MoveToElement();
            //string inner = xtr.ReadInnerXml();
            //if (inner !=null && inner.Contains("</Query>"))
            //{
            //    j.CorrelatedQuery = inner;
            //}

            //xtr.MoveToAttribute("right");

            return j;
        }
    }
}
